#include "pch.h"
#include "cLoggerInterface.h"
#include "Plugins/stringHelper/cStringHelper.h"

#include <iomanip>
#include <chrono>
#include <thread>
#include <memory.h>
#include <stdarg.h>

#include <string>
#include <fstream>
#include <sstream>
#include <vector>
#include <stdint.h>
#include <ctime>


#ifdef _WIN32
#include <direct.h>
#include <io.h>
#include <windows.h>
#else
#include <dirent.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#endif // WIN32

#ifdef USE_LOG4Z
#include "log4z.h"
#endif

namespace libtool_sdk
{
	const int LOG_BUF_LEN = (100 * 1024);

	bool cLoggerInterface::_bInit = false;
	int cLoggerInterface::_iRetentionDays = -1;

#ifdef USE_LOG4Z
	using namespace zsummer::log4z;
#endif

	void debug_WriteFormatLog(const char* szfmt, ...)
	{
		char chLog_buf[1024] = { 0 };
		memset(chLog_buf, 0, sizeof(chLog_buf));

		va_list arg_ptr;
		va_start(arg_ptr, szfmt);
		vsnprintf(chLog_buf, sizeof(chLog_buf), szfmt, arg_ptr);
		va_end(arg_ptr);

		auto now = std::chrono::system_clock::now();
		time_t in_time_t = std::chrono::system_clock::to_time_t(now);
		struct tm* ptmNow = localtime(&in_time_t);

		char chFileName[256] = { 0 };
		sprintf(chFileName, "loger_info.log");
		FILE *file = NULL;
		file = fopen(chFileName, "a+");
		//fopen_s(&file, chLogFileName, "a+");
		if (file)
		{
			fprintf(file, "[%04d-%02d-%02d %02d:%02d:%02d] %s\n",
				ptmNow->tm_year + 1900,
				ptmNow->tm_mon + 1,
				ptmNow->tm_mday,
				ptmNow->tm_hour,
				ptmNow->tm_min,
				ptmNow->tm_sec,
				chLog_buf);

			fclose(file);
			file = NULL;
		}
	}

	char* createLogBuffer(const char* format, va_list vlist)
	{
		char* buff = NULL;
		int bytes_needed = -1;
#ifdef _WIN32
		bytes_needed = _vscprintf(format, vlist);
		if (bytes_needed <= 0)
		{
			return buff;
		}
		buff = (char*)malloc(bytes_needed + 1);
		vsnprintf(buff, bytes_needed + 1, format, vlist);
#else
		bytes_needed = vasprintf(&buff, format, vlist);
#endif
		// if(bytes_needed > 0 )
		// {
		// 	_iLogBufSize += bytes_needed;
		// }
		return buff;
	}

	std::string removeTrailingSlash(const std::string& path)
	{
		std::string result = path;
		while (!result.empty() && (result.back() == '/' || result.back() == '\\')) {
			result.pop_back();
		}
		return result;
	}

	// 自定义 all_of 函数
	template<class InputIt, class UnaryPredicate>
	bool all_of(InputIt first, InputIt last, UnaryPredicate p)
	{
		for (; first != last; ++first) {
			if (!p(*first)) {
				return false;
			}
		}
		return true;
	}

	std::time_t dateStringToTime(const std::string& dateStr) 
	{
		std::tm tm = {};
		tm.tm_year = std::stoi(dateStr.substr(0, 4)) - 1900;
		tm.tm_mon = std::stoi(dateStr.substr(4, 2)) - 1;
		tm.tm_mday = std::stoi(dateStr.substr(6, 2));
		tm.tm_hour = 0;
		tm.tm_min = 0;
		tm.tm_sec = 0;
		return std::mktime(&tm);
	}

	cLoggerInterface::cLoggerInterface() :
		_iLogID(-1),
        _iLoglevel(log_level_trace),
		_iLogBufSize(0),
		_bDisplay(false),
		_bEnableLog(false),
		_pLogFile(NULL)
	{
		sprintf(_strLogerDir, "./log");
		memset(_strLoggerName, 0, sizeof(_strLoggerName));
		memset(_strLastLogerFileName, 0, sizeof(_strLastLogerFileName));
	}

	cLoggerInterface::~cLoggerInterface()
	{
        setEnableLog(false);
#ifndef USE_LOG4Z
		Close_logfile();
#endif
	}

	void cLoggerInterface::setLoggerName(const char* name)
	{
		if (NULL != name)
		{
            printf("setLoggerName: %s\n", name);
#ifdef _WIN32
			_snprintf(_strLoggerName,sizeof(_strLoggerName), "%s", name);
#else
            snprintf(_strLoggerName, sizeof(_strLoggerName), "%s", name);
#endif

#ifdef USE_LOG4Z
			_iLogID = ILog4zManager::getRef().createLogger(_strLoggerName));
			setLogPath(_strLogerDir.c_str());
			ILog4zManager::getRef().setLoggerMonthdir(_iLogID, true);
			//ILog4zManager::getRef().setLoggerFileLine(_iLogID, false);
#endif
		}
	}

	void cLoggerInterface::getLoggerName(char* buf, int bufLen)
	{
#ifdef _WIN32
		_snprintf(buf, bufLen, "%s", _strLoggerName);
#else
		snprintf(buf, bufLen, "%s", _strLoggerName);
#endif
	}

	void cLoggerInterface::setEnableLog(bool value)
	{
        printf("logger: %s  %s::  value = %d \n", _strLoggerName, __FUNCTION__, value);
		_bEnableLog = value;

#ifdef USE_LOG4Z
		startAll();
		if (_iLogID != -1)
		{
			ILog4zManager::getRef().enableLogger(_iLogID,value);
			ILog4zManager::getRef().setLoggerOutFile(_iLogID, value);
		}
#endif
	}

	void cLoggerInterface::setDisplay(bool value)
	{
#ifdef USE_LOG4Z
		if (_iLogID != -1)
		{
			ILog4zManager::getRef().setLoggerDisplay(_iLogID, value);
		}
#else
		_bDisplay = value;
#endif
	}

	void cLoggerInterface::setLogLevel(int value)
	{
#ifdef USE_LOG4Z
		if (_iLogID != -1)
		{
			ILog4zManager::getRef().setLoggerLevel(_iLogID, value);
		}
#else
		_iLoglevel = value;
#endif
	}

	void cLoggerInterface::setLogPath(const char* value)
	{
        printf("%s:: value = %s \n", __FUNCTION__, value);
#ifdef USE_LOG4Z
		if (_iLogID != -1)
		{
			ILog4zManager::getRef().setLoggerPath(_iLogID, value);
		}
#endif
		
#ifdef _WIN32
		_snprintf(_strLogerDir, sizeof(_strLogerDir), "%s", value);
#else
        snprintf(_strLogerDir, sizeof(_strLogerDir), "%s", value);
#endif

	}

	int cLoggerInterface::getLoggerID()
	{
#ifdef USE_LOG4Z
		if (_iLogID == -1)
		{
			_iLogID = ILog4zManager::getRef().createLogger(_strLoggerName);
		}	
#endif
		return _iLogID;
	}

#ifndef USE_LOG4Z
	void cLoggerInterface::writeLog(const char* logInfo)
	{
		auto now = std::chrono::system_clock::now();
		time_t in_time_t = std::chrono::system_clock::to_time_t(now);
		// struct tm* ptmNow = localtime(&in_time_t);
		struct tm tmNow;
#ifdef _WIN32
		localtime_s(&tmNow, &in_time_t);		
#else		
		localtime_r(&in_time_t, &tmNow);		
#endif
		struct tm* ptmNow = &tmNow;

		auto duration = now.time_since_epoch();
		long long  iMillisecond = std::chrono::duration_cast<std::chrono::milliseconds>(duration).count();

		std::string strDirName = std::string(_strLogerDir);
		if (strDirName.empty())
		{
			//strDirName = Helper::Tool_get_dll_path();
			return;
		}

		if (!s_check_is_directory(strDirName.c_str()))
		{
			s_createRecursionDir(strDirName.c_str());

			debug_WriteFormatLog("try create dir %s", strDirName.c_str());
		}
		
		if(_iRetentionDays != -1)
		{
			//Performed once a day
			if(_llLastCheckTime == 0 || (iMillisecond - _llLastCheckTime) > 86400000)
			{
				s_deleteOldLogFiles(strDirName.c_str(), _iRetentionDays);
				_llLastCheckTime = iMillisecond;
			}
		}

		char chFileName[512] = { 0 };
		sprintf(chFileName, "%04d%02d%02d_%s.log",
			ptmNow->tm_year + 1900,
			ptmNow->tm_mon + 1,
			ptmNow->tm_mday,
			_strLoggerName);

		char chTempName[512] = { 0 };
		sprintf(chTempName, "%s/%s",strDirName.c_str(), chFileName);

		char chFinalString[512] = {0};
		Helper::Tool_ReplaceStringInStd(chTempName, "//", "/", chFinalString, sizeof(chFinalString));

		if(strcmp(chFinalString, _strLastLogerFileName) != 0 || _pLogFile == NULL)
		{
			memset(_strLastLogerFileName, 0, sizeof(_strLastLogerFileName));
			strcpy(_strLastLogerFileName, chFinalString);
			Init_logfile(chFinalString);
		}

		if (_pLogFile)
		{
			fprintf((FILE*)_pLogFile, "[%04d-%02d-%02d %02d:%02d:%02d.%03lld] ",
				ptmNow->tm_year + 1900,
				ptmNow->tm_mon + 1,
				ptmNow->tm_mday,
				ptmNow->tm_hour,
				ptmNow->tm_min,
				ptmNow->tm_sec,
				iMillisecond % 1000);

			fprintf((FILE*)_pLogFile, "[tid %llu]", getCurrentThreadID());
	#ifdef LIB_VERSION
			fprintf((FILE*)_pLogFile, "[lib %s]", LIB_VERSION);
	#endif
			fprintf((FILE*)_pLogFile, "%s\n", logInfo);
		}
	}

	void cLoggerInterface::logFmt(int level, const char* format, ...)
	{
		if (!_bEnableLog)
        {
            //printf("%s:: _bEnableLog = %d \n", __FUNCTION__, _bEnableLog);
			return;
        }

		if (level < _iLoglevel)
		{
            //printf("%s:: level < _iLoglevel \n", __FUNCTION__);
			return;
		}

		char *pLogBuf = NULL;

		va_list arg_ptr;
		va_start(arg_ptr, format);
		// vsnprintf(pLogBuf, LOG_BUF_LEN, format, arg_ptr);
		pLogBuf = createLogBuffer(format, arg_ptr);
		va_end(arg_ptr);

		if (pLogBuf)
		{
			writeLog(pLogBuf);

			free(pLogBuf);
			pLogBuf = NULL;
		}
	}

#else
	void cLoggerInterface::logFmt_log4z(int level,const char* file,const int line,const char*  format, ...)
	{
		using namespace zsummer;
		using namespace zsummer::log4z;
		
		char *pLogBuf = NULL;

		va_list arg_ptr;
		va_start(arg_ptr, format);
		// vsnprintf(pLogBuf, LOG_BUF_LEN, format, arg_ptr);
		pLogBuf = createLogBuffer(format, arg_ptr);
		va_end(arg_ptr);

		if (pLogBuf)
		{
			LOG_STREAM(getLoggerID(), level, file, line, pLogBuf);

			free(pLogBuf);
			pLogBuf = NULL;
		}
	}
#endif

	void cLoggerInterface::startAll()
	{
		if (!_bInit)
		{
#ifdef USE_LOG4Z		
			ILog4zManager::getRef().setLoggerPath(LOG4Z_MAIN_LOGGER_ID, "./log");
			// ILog4zManager::getRef().setLoggerPath(LOG4Z_MAIN_LOGGER_ID, _strLogerDir.c_str());
			ILog4zManager::getRef().setLoggerOutFile(LOG4Z_MAIN_LOGGER_ID, false);
			//ILog4zManager::getRef().setLoggerFileLine(LOG4Z_MAIN_LOGGER_ID, false);
			ILog4zManager::getRef().start();
#endif
			_bInit = true;
		}
	}

	void cLoggerInterface::stopAll()
	{
		if (_bInit)
		{
#ifdef USE_LOG4Z
			ILog4zManager::getRef().stop();
#endif
			_bInit = false;
		}
	}

	bool cLoggerInterface::s_check_is_directory(const char* dirName)
	{
#ifdef _WIN32
		if ((_access(dirName, 0)) == 0)
		{
			return true;
		}
#else
		if ((access(dirName, F_OK)) == 0)
		{
			return true;
		}
#endif // WIN32

		return false;
	}

	bool cLoggerInterface::s_createRecursionDir(const char* path)
	{
		std::string cleanPath = removeTrailingSlash(path);
		std::vector<std::string> directories;
		std::stringstream ss(cleanPath);
		std::string dir;

		// 将路径按分隔符分割成多个目录名
		while (std::getline(ss, dir, '/')) {
			if (!dir.empty()) {
				directories.push_back(dir);
			}
		}

		std::string currentPath;
		for (const auto& directory : directories) {
			if (!currentPath.empty()) {
				currentPath += "/";
			}
			currentPath += directory;

#ifdef _WIN32
			// Windows 系统下使用 CreateDirectory 函数创建目录
			if (CreateDirectory(currentPath.c_str(), NULL) == 0) {
				// 如果目录创建失败且错误码不是 ERROR_ALREADY_EXISTS（目录已存在），则返回 false
				if (GetLastError() != ERROR_ALREADY_EXISTS) {
					return false;
				}
			}
#else
			// 类 Unix 系统下使用 mkdir 函数创建目录
			if (mkdir(currentPath.c_str(), 0777) == -1) {
				// 如果目录创建失败且错误码不是 EEXIST（目录已存在），则返回 false
				if (errno != EEXIST) {
					return false;
				}
			}
#endif
		}
		return true;
	}

	void cLoggerInterface::s_fixPath(const char* fixPath)
	{
		std::string path(fixPath);
		if (path.empty()) { return; }
		for (std::string::iterator iter = path.begin(); iter != path.end(); ++iter)
		{
			if (*iter == '\\') { *iter = '/'; }
		}
		if (path.at(path.length() - 1) != '/') { path.append("/"); }
	}

	unsigned int cLoggerInterface::s_getFileSize(const char* fileName)
	{
		FILE* fp = fopen(fileName, "r");
		if (!fp) { return 0; }
		fseek(fp, 0L, SEEK_END);
		long isize = ftell(fp);
		fclose(fp);

		return isize;
	}
	void cLoggerInterface::s_setRetentionDays(int days)
	{
		_iRetentionDays = days;
	}
#ifdef _WIN32
	void cLoggerInterface::s_deleteOldLogFiles(const char *dirPath, int days)
	{
		std::string directory(dirPath);
		std::time_t now = std::time(nullptr);
		std::tm *now_tm = std::localtime(&now);
		now_tm->tm_mday -= days;
		std::time_t threshold = std::mktime(now_tm);

		std::vector<std::string> filesToDelete;

		std::string searchPath = directory + "\\*.log";
		WIN32_FIND_DATAA findData;
		HANDLE hFind = FindFirstFileA(searchPath.c_str(), &findData);
		if (hFind == INVALID_HANDLE_VALUE)
		{
			debug_WriteFormatLog("No log files found in directory: %s", directory.c_str());
			return;
		}

		do
		{
			std::string filename = findData.cFileName;
			std::string fullPath = directory + "\\" + filename;
			// Check whether the file name prefix is in date format
			if (filename.length() >= 8 && all_of(filename.begin(), filename.begin() + 8, ::isdigit))
			{
				std::string dateStr = filename.substr(0, 8);
				std::time_t fileDate = dateStringToTime(dateStr);
				if (fileDate < threshold)
				{
					filesToDelete.push_back(fullPath);
				}
			}

			FILETIME ftCreation, ftAccess, ftWrite;
			HANDLE hFile = CreateFileA(fullPath.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
			if (hFile != INVALID_HANDLE_VALUE)
			{
				if (GetFileTime(hFile, &ftCreation, &ftAccess, &ftWrite))
				{
					SYSTEMTIME stLastWriteTime;
					FileTimeToSystemTime(&ftWrite, &stLastWriteTime);
					std::tm tm = {};
					tm.tm_year = stLastWriteTime.wYear - 1900;
					tm.tm_mon = stLastWriteTime.wMonth - 1;
					tm.tm_mday = stLastWriteTime.wDay;
					tm.tm_hour = stLastWriteTime.wHour;
					tm.tm_min = stLastWriteTime.wMinute;
					tm.tm_sec = stLastWriteTime.wSecond;
					std::time_t fileCreationTimestamp = std::mktime(&tm);
					if (fileCreationTimestamp < threshold)
					{
						filesToDelete.push_back(fullPath);
					}
				}
				CloseHandle(hFile);
			}
		} while (FindNextFileA(hFind, &findData) != 0);

		FindClose(hFind);

		for (const auto &file : filesToDelete)
		{
			WIN32_FIND_DATAA findData;
			HANDLE hFind = FindFirstFileA(file.c_str(), &findData);
			if (hFind != INVALID_HANDLE_VALUE)
			{
				FindClose(hFind);
				if (DeleteFileA(file.c_str()))
				{
					debug_WriteFormatLog("Deleted file: %s", file.c_str());
				}
				else
				{
					debug_WriteFormatLog("Failed to delete file: %s", file.c_str());
				}
			}
			else
			{
				debug_WriteFormatLog("File not found: %s", file.c_str());
			}
		}
	}
#else
	void cLoggerInterface::s_deleteOldLogFiles(const char *dirPath, int days)
	{
		std::string directory(dirPath);
		std::time_t now = std::time(nullptr);
		std::tm *now_tm = std::localtime(&now);
		now_tm->tm_mday -= days;
		std::time_t threshold = std::mktime(now_tm);

		std::vector<std::string> filesToDelete;

		DIR *dir = opendir(directory.c_str());
		if (dir == nullptr)
		{
			debug_WriteFormatLog("Failed to open directory: %s", directory.c_str());
			return;
		}

		struct dirent *entry;
		while ((entry = readdir(dir)) != nullptr)
		{
			std::string filename = entry->d_name;
			if (filename.length() > 4 && filename.substr(filename.length() - 4) == ".log")
			{
				std::string fullPath = directory + "/" + filename;
				// Check whether the file name prefix is in date format
				if (filename.length() >= 8 && all_of(filename.begin(), filename.begin() + 8, ::isdigit))
				{
					std::string dateStr = filename.substr(0, 8);
					std::time_t fileDate = dateStringToTime(dateStr);
					if (fileDate < threshold)
					{
						filesToDelete.push_back(fullPath);
					}
				}

				// Check the date the file was last modified
				struct stat fileStat;
				if (stat(fullPath.c_str(), &fileStat) == 0)
				{
					std::time_t fileCreationTimestamp = fileStat.st_mtime;
					if (fileCreationTimestamp < threshold)
					{
						filesToDelete.push_back(fullPath);
					}
				}
			}
		}

		closedir(dir);

		// Delete files in the queue
		for (const auto &file : filesToDelete)
		{
			if (access(file.c_str(), F_OK) == 0)
			{
				if (remove(file.c_str()) == 0)
				{
					debug_WriteFormatLog("Deleted file: %s", file.c_str());
				}
				else
				{
					debug_WriteFormatLog("Failed to delete file: %s", file.c_str());
				}
			}
			else
			{
				debug_WriteFormatLog("File not found: %s", file.c_str());
			}
		}
	}

#endif
	
	unsigned long long cLoggerInterface::getCurrentThreadID()
	{
#ifdef _WIN32
		return GetCurrentThreadId();
#else
		return pthread_self();
#endif
	}
	
//	char* cLoggerInterface::createLogBuffer(const char* format, va_list vlist)
//	{
//		char* buff = NULL;
//		int bytes_needed = -1;
//#ifdef _WIN32
//		bytes_needed = _vscprintf(format, vlist);
//		if(bytes_needed <= 0)
//		{
//			return buff;
//		}
//		buff = (char*)malloc(bytes_needed+1);
//		vsnprintf(buff, bytes_needed+1, format, vlist);
//#else
//		bytes_needed = vasprintf(&buff, format, vlist);
//#endif
//		// if(bytes_needed > 0 )
//		// {
//		// 	_iLogBufSize += bytes_needed;
//		// }
//		return buff;
//	}
	void cLoggerInterface::Init_logfile(const char * filename)
	{
		printf("Init_logfile %s\n", filename);
		Close_logfile();
		_pLogFile = fopen(filename, "a+");
		if (_pLogFile)
		{
			setvbuf((FILE*)_pLogFile, NULL, _IONBF, 0);	// no buffer
		}
	}

	void cLoggerInterface::Close_logfile()
	{
		printf("Close_logfile\n");
		if (_pLogFile)
		{
			fflush((FILE*)_pLogFile);
			fclose((FILE*)_pLogFile);
			_pLogFile = NULL;
		}
	}
	void cLoggerInterface::Flush_logfile()
	{
		if (_pLogFile)
		{
			fflush((FILE*)_pLogFile);
		}
	}
}
